home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / pxsrc_21.zip / PAKSORTM.C < prev    next >
Text File  |  1990-09-18  |  14KB  |  550 lines

  1. /*
  2.         PAKSORTM.C         Sort contents of .ARC archive.
  3.         Copyright 1989 by Jeffrey J. Nonken
  4.         Released to public domain.
  5.  
  6.         Modified from PAKSORT.C: Copyright 1988, Michael J. Housky
  7.         Released to the Public Domain ... *no* rights reserved.
  8.  
  9.         I ripped off Mike's code from the stand-alone program PAKSORT in
  10.         order to include it into PolyXarc as a module. The only changes I
  11.         made were: stripped the main() function out, removed the order[]
  12.         variable and hard-coded the sort order, and added code to record
  13.         the highest compression type value (so I can tell whether to use
  14.         ARCE, PKUNPAK, or PAK) to discombobulate. I also now allocate the
  15.         buffer, rather than having a fixed buffer size.
  16. */
  17.  
  18. /*
  19.     Update log:
  20.  
  21.     18 September 1990: added support for type 1 headers (which are 4 bytes
  22.         shorter than all other header types); added support for version 6
  23.         archives. Version 6 archives have some advanced stuff that version
  24.         5 archives don't have, and some of it is position dependent, so we
  25.         WON'T sort version 6 archives. Since PKPAK never got past version 5
  26.         headers, that's not a problem. Type 1 headers are considered obsolete,
  27.         but I wouldn't want this to bomb for such a stupid reason. Removed
  28.         .olen from the local header stuff; it's not used for anything once
  29.         we've tested the header for validity. jjn
  30.     22 March 1990: added Amiga stuff. jjn & smp
  31.     14 November 1989: modified from PAKSORT.C. jjn
  32.  
  33. PAKSORT.C update log:
  34.     Version 1.1, 24 January 1989: changed default extension .PAK to .ARC. jjn
  35.     Version 1.0, 25 August 1988: First version.
  36. */
  37.  
  38. #include <string.h>
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41.  
  42. #ifndef AMIGA
  43. #    include <io.h>
  44. #endif
  45.  
  46. #ifdef AMIGA
  47. #define _NEAR_
  48. #endif
  49.  
  50. #ifdef TURBOC
  51. #define _NEAR_
  52. #endif
  53.  
  54. #ifdef MSC
  55. #include <malloc.h>
  56. #define _NEAR_ near
  57. #endif
  58.  
  59.  
  60. #include "p_common.h"
  61. #include "pakmdefn.h"
  62.  
  63. #define MAX_BUFSIZE     32760
  64.  
  65. typedef struct                    /* stored directory format: */
  66. {
  67.     int32        floc;            /* byte location of header in file */
  68.     int32        slen;            /* stored (packed) length of file */
  69. /*     int32        olen;             * original (unpacked) length of file */
  70.     word16        ddate;            /* DOS date of update */
  71.     word16        dtime;            /* DOS time of update */
  72.     char        fname[13];        /* filename */
  73.     unsigned char type;            /* Header type. */
  74. } pak_dir;
  75.  
  76. /* ------------ Global data: */
  77.  
  78.  
  79. byte            * _NEAR_ pak_buf;        /* buffer for reading pakfile data */
  80. pak_hdr         _NEAR_ fhdr;            /* buffer for reading pakfile header */
  81. long            _NEAR_ pakloc;            /* location in input file */
  82. long            _NEAR_ pakloc,
  83.                 _NEAR_ paklen;            /* length of input file */
  84. int             _NEAR_ memory;
  85. int16            _NEAR_ highest_type;
  86. int16            _NEAR_ version6;        /* True if version 6 header. */
  87.  
  88. extern int        _NEAR_ quiet;
  89.  
  90.  
  91.  
  92. /*****************************************************************************
  93. This will turn an Intel-format integer into a Motorola-format integer.
  94. *****************************************************************************/
  95. #ifdef MOTOROLA
  96. void swap_Intel(char *address, int numbytes)
  97. {
  98. register int i; /* Ascending index. */
  99. register int j; /* Descending index. */
  100. char    c;        /* Temporary storage for the swap. */
  101.  
  102.     for (i = 0, j = numbytes - 1; i < j; ++i, --j)
  103.     {
  104.         c = address[i];
  105.         address[i] = address[j];
  106.         address[j] = c;
  107.     }
  108. }
  109. #endif
  110.  
  111. /*
  112.         read_pakhdr:    Read header of .ARC member file
  113.  
  114. Reads next header sequentially from file.
  115.  
  116. Returns 0 if good header, and global fhdr has header.
  117. Returns +1 if end of file marker found.
  118. Returns -1 if bad header or read error.
  119.  
  120. */
  121.  
  122. int read_pakhdr(FILE *fp)
  123. {
  124.     register int i;
  125.  
  126.     i = fgetc(fp);                        /* get file marker */
  127.     if ( i != PAK_MARK )                /* error if not correct */
  128.         return -1;
  129.  
  130.     i = fread( (char*)&fhdr, 1, PAK_HDRLEN, fp );
  131.                                         /* read header, follows mark */
  132. #ifdef MOTOROLA
  133.     swap_Intel((char *)&fhdr.slen ,sizeof(fhdr.slen) );
  134.     swap_Intel((char *)&fhdr.ddate,sizeof(fhdr.ddate));
  135.     swap_Intel((char *)&fhdr.dtime,sizeof(fhdr.dtime));
  136.     swap_Intel((char *)&fhdr.crc  ,sizeof(fhdr.crc)  );
  137.     swap_Intel((char *)&fhdr.olen ,sizeof(fhdr.olen) );
  138. #endif
  139.  
  140.     if ( i>=1 && fhdr.type==0 )         /* type 0 header is an EOF mark */
  141.         return 1;                        /* end of file mark seen */
  142.  
  143.     if (fhdr.type == 1)                    /* Header type 1 is 4 bytes short. */
  144.     {
  145.         fhdr.olen = fhdr.slen;            /* Use the stored length. */
  146.         fseek(fp,-4l,1);                /* Seek relative backwards by 4. */
  147.     }
  148.     
  149.     if ( i!=PAK_HDRLEN || fhdr.slen<0L)
  150.         return -1;                        /* invalid header seen */
  151.  
  152.     if (fhdr.type < 20 && fhdr.olen < fhdr.slen)
  153.         return -1;                        /* invalid header seen */
  154.  
  155.     pakloc = ftell(fp);                 /* note start of data and test */
  156.     if ( pakloc+fhdr.slen > paklen )    /*    for all data present */
  157.         return -1;                        /* no: presume truncated file */
  158.     return 0;
  159.  
  160. } /* read_pakhdr */
  161.  
  162.  
  163. /*
  164.         pak_getdir:     Build directory of .ARC file headers.
  165.  
  166. Sequentially read and validate all headers within pakfile. Build malloc-ed
  167. table of headers found.
  168.  
  169. Returns:
  170.          0 = success, *pdirp = pointer to malloc-ed directory.
  171.          1 = out of memory;
  172.         -1 = read/seek error or bad header.
  173. */
  174.  
  175. int pak_getdir( FILE *fp, pak_dir **pdirp )
  176. {
  177.     register    int i;
  178.     int         n,k;
  179.     long        hdrloc;
  180.     pak_dir     *pd;
  181.  
  182. /* Read the first header: */
  183.  
  184.     highest_type = 0;                /* Init the highest compression type. */
  185.     version6 = FALSE;                /* Haven't found version 6 stuff yet. */
  186.     hdrloc = ftell(fp);
  187.  
  188.     i = read_pakhdr(fp);                /* Get the first header. */
  189.     if ( i )
  190.     {
  191.         printf("Invalid header seen.\n");
  192.         return -1;
  193.     }
  194.  
  195.     pd = (pak_dir*) malloc( (k=32)*sizeof(pak_dir) );
  196.     if ( pd==NULL )
  197.     {
  198.         printf("Out of memory.\n");
  199.         memory = 1;
  200.         return -1;                        /* out of memory error */
  201.     }
  202.     n = 0;
  203.  
  204.     do                    /* loop once for each new entry found, with */
  205.     {                    /* n=#headers found, k=directory size */
  206.  
  207.         if ( n >= k )                    /* test for current table full */
  208.         {
  209.             /* add 10 more entries to directory: */
  210.             pd = (pak_dir*) realloc( (char*)pd, (k+=16)*sizeof(pak_dir) );
  211.             if ( pd==NULL )
  212.             {
  213.                 printf("Out of memory.\n");
  214.                 memory = 1;
  215.                 return -1;                /* out of memory error */
  216.             }
  217.         }
  218.  
  219.         if (fhdr.type < 20)
  220.         {
  221.             if (fhdr.type > (char)highest_type)
  222.                 highest_type = fhdr.type;
  223.         }
  224.         else
  225.             version6 = TRUE;
  226.         pd[n].type    = fhdr.type;        /* Copy header type. */
  227.         pd[n].floc    = hdrloc;            /* store header location */
  228.         pd[n].slen    = fhdr.slen;        /* copy stored length */
  229. /*         pd[n].olen    = fhdr.olen;         * copy original length */
  230.         pd[n].ddate = fhdr.ddate;        /* copy DOS date */
  231.         pd[n].dtime = fhdr.dtime;        /* copy DOS time */
  232.         strcpy(pd[n].fname,fhdr.fname); /* copy file name */
  233.         ++n;                            /* and increment file count */
  234.  
  235.         hdrloc += fhdr.slen+PAK_HDRLEN+1;
  236.         if (fhdr.type == 1)                /* Type 1 header is shorter! */
  237.             hdrloc -= 4;                /* If type 1, reduce by 4. */
  238.     
  239.         i = fseek( fp, hdrloc, SEEK_SET );
  240.         if ( i )
  241.         {
  242.             printf("Seek error.\n");
  243.             free( (char*)pd );
  244.             return -1;
  245.         }
  246.  
  247.         i = read_pakhdr(fp);
  248.  
  249.     } while ( i==0 );
  250.  
  251.     if ( i<0 )                            /* test for error */
  252.     {
  253.         printf("Invalid header seen.\n");
  254.         free( (char*)pd );
  255.         return -1;
  256.     }
  257.     *pdirp = pd;
  258.     return n;
  259.  
  260. } /* pak_getdir */
  261.  
  262.  
  263. /*
  264.         dir_cmp:        Directory entry compare function for sort.
  265.  
  266. Input:
  267.         lp:     pointer to "left" entry
  268.         rp:     pointer to "right" entry
  269. Return:
  270.         0 if entries are equal
  271.         positive if left "greater than" right
  272.         negative if left "less than" right
  273. */
  274.  
  275. int dir_cmp(pak_dir *lp, pak_dir *rp)
  276. {
  277.     register int j;
  278.  
  279.                                 /* compare unsigned date: */
  280.     j = (lp->ddate > rp->ddate) - (lp->ddate < rp->ddate);
  281.     if (!j)
  282.                                 /* compare unsigned time: */
  283.         j = (lp->dtime > rp->dtime) - (lp->dtime < rp->dtime);
  284.     if (!j)
  285.                                 /* compare full fileid */
  286.         j = strcmp( lp->fname, rp->fname );
  287.     return j;
  288.  
  289. } /* dir_cmp */
  290.  
  291.  
  292. /*
  293.         pak_sort:        Sort one .ARC file.
  294.  
  295. A simple (slow, but small and sure) routine is used here to
  296. sort the directory without the overhead of qsort and recursion.
  297.  
  298. If this sort is replaced with an unstable sort (like quicksort) then
  299. the dir_cmp function should be changed to return compare of original
  300. file location if all fields compare equal. This will preserve
  301. sort stability ... i.e. input order is preserved on equal entries.
  302.  
  303. Return value is 0 if all files were originally in order,
  304.  or 1 if output order is different from input order.
  305.  
  306. */
  307.  
  308. int pak_sort( pak_dir *pdir, int n)
  309. {
  310.     register    int i,j;
  311.     int         i0,j0,k,r;
  312.     pak_dir     tdir;
  313.  
  314.     r = 0;
  315.     for ( j0=1; j0<n; ++j0 )
  316.     {
  317.         i = i0 = j0-1;                    /* starting entry # to compare */
  318.         for ( j=j0; j<n; ++j )
  319.         {
  320.             k = dir_cmp( pdir+i, pdir+j);
  321.             if ( k>0 )
  322.             {                            /* out of order */
  323.                 i = j;                    /* will swap at end */
  324.             }
  325.         }
  326.         if ( i != i0 )                    /* if out of order */
  327.         {
  328.             tdir = pdir[i];             /* swap smallest entry */
  329.             pdir[i] = pdir[i0];         /* (at most n-1 times) */
  330.             pdir[i0] = tdir;
  331.             r = 1;                        /* tell caller that input was */
  332.                                         /* not in order */
  333.         }
  334.     }
  335.     return r;
  336.  
  337. } /* pak_sort */
  338.  
  339.  
  340. /*
  341.         pak_write:        Copy packed source files to dest in sorted order.
  342. */
  343.  
  344. int pak_write( FILE *src, FILE *dst, pak_dir* dirp, int n )
  345.  
  346. /*     FILE        *src;             * opened source file */
  347. /*     FILE        *dst;             * opened dest file */
  348. /*     pak_dir     *dirp;             * sorted directory of source */
  349. /*     int         n;                 * number of entries in source directory */
  350. {
  351.     register int i,j;
  352.     int         k;
  353.     int         buf_size;
  354.     long        len,start,end;
  355.  
  356.     buf_size = MAX_BUFSIZE;
  357.     do
  358.     {
  359.         pak_buf = malloc(buf_size);
  360.         if (pak_buf == NULL)
  361.             buf_size >>= 1;
  362.     } while (pak_buf == NULL && buf_size > 0);
  363.     if (pak_buf == NULL)
  364.     {
  365.         printf("Out of memory.\n");
  366.         memory = 1;
  367.         return -1;
  368.     }
  369.  
  370.     for ( i=0; i<n; ++i )
  371.     {                            /* once for each member file */
  372.  
  373.         len = dirp[i].slen + 1 + 
  374.                     ((1 == dirp[i].type) ? (PAK_HDRLEN - 4) : PAK_HDRLEN);
  375.  
  376.         if (!quiet)
  377.         {
  378.             printf("  Copying: %-13s - loc=%-6ld  len=%-6ld\n",
  379.                     dirp[i].fname,
  380.                     dirp[i].floc,
  381.                     len);
  382.         }
  383.  
  384.         /* point to start of member, test for error: */
  385.         j = fseek(src, dirp[i].floc, SEEK_SET);
  386.         if ( j )
  387.         {
  388.             printf("Seek error.\n");
  389.             free(pak_buf);
  390.             return -1;
  391.         }
  392.  
  393.         /* copy member to destination: */
  394.         for ( end=0; end<len; )
  395.         {
  396.             start = end;
  397.             end = start+(k=buf_size);
  398.             if (end>len)
  399.                 k = (int)((end=len)-start);
  400.             j = fread( pak_buf, 1, k, src );
  401.             if ( j != k )
  402.             {
  403.                 printf("Read error.\n");
  404.                 free(pak_buf);
  405.                 return -1;
  406.             }
  407.             j = fwrite( pak_buf, 1, k, dst );
  408.             if ( j != k )
  409.             {
  410.                 printf("Write error.\n");
  411.                 free(pak_buf);
  412.                 return -1;
  413.             }
  414.         }
  415.     }
  416.  
  417.     free(pak_buf);
  418.     /* Add EOF mark to (end of) destination: */
  419.     fputc( PAK_MARK, dst );
  420.     fputc( '\0', dst );
  421.     if ( ferror(dst) )
  422.     {
  423.         printf("Write error.\n");
  424.         return -1;
  425.     }
  426.  
  427.     return 0;
  428.  
  429. } /* pak_write */
  430.  
  431.  
  432.  
  433. /*
  434.         pak_dosort:     Read/sort/rewrite one .ARC file.
  435.  
  436. Returns 0 if successful, or errorlevel for DOS on error:
  437.    1 = Can't open/read source. (missing or invalid pakfile)
  438.    2 = Can't create/write dest (maybe disk or directory full)
  439.    3 = Can't delete old source file. (read-only)
  440.    4 = Can't rename temp file to source name. (???)
  441.    5 = Out of memory.
  442. */
  443.  
  444. int pak_dosort(char *fpath, char *fn, int sort)
  445. {
  446.     register int i,j;
  447.     int         n;
  448.     pak_dir     *pdir;            /* pointer to (malloced) directory */
  449.                                 /*    built by pak_getdir */
  450.     FILE        *src;            /* source file */
  451.     FILE        *dst;            /* source file */
  452.     char        *fnptr;         /* location of filename in fname */
  453.  
  454. #ifdef AMIGA
  455.     char        srcname[108];
  456.     char        fname[108];
  457. #else
  458.     char        srcname[64];    /* save source filename */
  459.     char        fname[64];
  460. #endif
  461.     memory = 0;
  462.     strcpy(fname,fpath);
  463.     fnptr = fname + strlen(fname);
  464.     strcpy(fnptr,fn);                    /* make full path to file */
  465.     if (!quiet)
  466.         printf("Searching:  %s\n",fname); /* and identify */
  467.     src = fopen( fname, "rb" );         /* and open file */
  468.     if ( src==NULL )
  469.     {
  470.         printf("Can't open %s.\n", fname);
  471.         return 1;
  472.     }
  473. #ifdef AMIGA
  474.     fseek (src, 0L, SEEK_END);
  475.     paklen = ftell(src);
  476.     fseek (src, 0L, SEEK_SET);
  477. #else
  478.     paklen = filelength(fileno(src));    /* get file length in global */
  479. #endif
  480.     pdir = NULL;                        /* clear directory pointer */
  481.     n = pak_getdir( src, &pdir );        /* get directory */
  482.     if (memory)
  483.         return 5;
  484.     if ( n<1 )
  485.     {
  486.         printf("Invalid file format, not changed.\n");
  487.         fclose(src);
  488.         return 1;
  489.     }
  490.     if (version6 || !sort)                /* Don't sort if version 6 format. */
  491.     {
  492.         free( (char*)pdir );
  493.         fclose(src);                    /* unconditionally close source */
  494.         return 0;
  495.     }
  496.  
  497.     i = pak_sort( pdir, n);
  498.     if ( i==0 )
  499.     {
  500.         printf("Already in order, not changed.\n");
  501.         free( (char*)pdir );
  502.         fclose(src);                        /* unconditionally close source */
  503.         return 0;
  504.     }
  505.  
  506. /* pakfile is not in order, create sorted copy & rename: */
  507.  
  508.     strcpy( srcname, fname );
  509.     strcpy( fnptr, "PAKSRTMP.$$$" );
  510.     dst = fopen( fname, "wb" );
  511.     if ( dst==NULL )
  512.     {
  513.         printf("Can't create: %s\n", fname );
  514.         free( (char*)pdir );
  515.         return 1;
  516.     }
  517.  
  518.     j = pak_write( src, dst, pdir, n ); /* copy source to dest tempfile */
  519.     fclose(src);                        /* unconditionally close source */
  520.     free( (char*)pdir );                /*    and release directory mem */
  521.     i = fclose(dst);                    /* close dest file */
  522.  
  523.     if ( j || i )                        /* if error on write or close */
  524.     {
  525.             if ( !j )                    /* identify close error */
  526.                 printf("Can't close: %s\n",fname);
  527.             unlink(fname);
  528.             return 2;
  529.     }
  530.  
  531.     j = unlink(srcname);                /* delete source */
  532.     if ( j )
  533.     {                                    /* can't delete, read-only? */
  534.         printf( "Can't delete original.\n");
  535.         unlink(fname);                    /* delete sorted temp copy */
  536.         return 3;                        /* return error */
  537.     }
  538.     j = rename( fname, srcname );
  539.     if ( j )
  540.     {
  541.         printf("Rename error: %d, original unsorted data lost!\n");
  542.         printf("Sorted data retained in temp output file for recovery:\n");
  543.         printf("%s\n",fname);
  544.         return 4;
  545.     }
  546.  
  547.     return 0;
  548.  
  549. } /* pak_dosort */
  550.